package thesis.javaclass;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.Scanner;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.Timer;

public class TaxiListener extends JFrame implements ActionListener {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private JFrame thisFrame = this;
	private static ServerSocket servSock;
	private JPanel panel = null;
	private JTextField messageBox = null;
	private JButton startBtn = null;
	private JTextArea conversationBox = null;
	private JLabel portLb = null;
	private JButton okBtn = null;
	private JButton cancelBtn = null;
	private boolean ok = false;
	private boolean cancel = false;
	private JTextField imeiText = null;
	private static String imei = "";
	private int count = 0;
	private int stepCount = 1;

	// for buffer customer
	private String bufferMessage = "";
	private String bufferPhone;
	private String bufferAddress;
	private String bufferCoordinate;
	private boolean busy = false;
	private boolean isBuffer = false;

	private Scanner input; // stream for get data
	private PrintWriter output; // stream for send data
	private Socket socket = null; //
	private String message; // variable for save input string

	private String phone;
	private String address;
	private String coordinate;

	public TaxiListener() {
		setTitle("Taxi listener box");
		conversationBox = new JTextArea(8, 10);
		conversationBox.setEditable(false);
		add(new JScrollPane(conversationBox), BorderLayout.NORTH);

		okBtn = new JButton("Accept");
		okBtn.setEnabled(false);
		okBtn.addActionListener(this);
		cancelBtn = new JButton("Deny");
		cancelBtn.setEnabled(false);
		cancelBtn.addActionListener(this);

		panel = new JPanel();
		messageBox = new JTextField(4);
		messageBox.setSize(4, 2);
		startBtn = new JButton("Start server");
		startBtn.addActionListener(this);
		portLb = new JLabel("Port :");
		panel.add(new JLabel("Emei : "));
		imeiText = new JTextField(10);
		panel.add(imeiText);
		panel.add(portLb);
		panel.add(messageBox);
		panel.add(startBtn);
		panel.add(okBtn);

		panel.add(cancelBtn);
		add(panel, BorderLayout.SOUTH);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		addWindowListener(new WindowAdapter() {

			@Override
			public void windowClosing(WindowEvent e) {
				if (servSock != null) {
					System.out.println("Frame is closing");
					try {
						servSock.close();
						System.exit(0);

					} catch (IOException e1) {
						System.out.println("Stop thread");
					}
					System.out.println("Frame is closed success");
				}

			}
		});

	}

	public static void main(String[] args) {

		TaxiListener taxiListener = new TaxiListener();
		taxiListener.setSize(560, 205);
		taxiListener.setVisible(true);

	}

	@Override
	public void actionPerformed(ActionEvent event) {
		if (event.getSource().equals(startBtn)) {
			if (messageBox.getText() == null || messageBox.getText().equals("")
					|| imeiText.getText().equals("")) {
				JOptionPane.showMessageDialog(this,
						"Please type valid imei and port !!");
				return;
			}
			imei = imeiText.getText();
			int port = -1;
			try {
				port = Integer.parseInt(messageBox.getText());
				System.out.println("Opening port... " + port + " success\n");
				startBtn.setEnabled(false);
				// okBtn.setEnabled(true);
				// cancelBtn.setEnabled(true);
			} catch (Exception e) {
				conversationBox.append("Invalid port\n");
				// finished = true;
			}
			if (servSock == null) {
				try {
					servSock = new ServerSocket(port);
				} catch (IOException ioEx) {
					System.out.println("Unable to attach to port : " + port
							+ "!");
					System.exit(1);
				}

				System.out.println("Open success! Waiting forever !!!");
			} else {
				conversationBox.append("Continue waiting forever !!!\n");
			}

			Thread t = new Thread() {

				public void run() {
					while (true) {

						try {
							socket = servSock.accept();
							if (isBuffer)
								socket.close();
							else {
								if (!busy) {
									CurrentCustomerHandler current = new CurrentCustomerHandler();
									Thread curThread = new Thread(current);
									curThread.start();
								} else {
									BufferCustomerHandler buff = new BufferCustomerHandler();
									Thread buffThread = new Thread(buff);
									buffThread.start();
								}
							}

						} catch (SocketException se) {
							System.out.println("Close server socket");
							break;
						} catch (IOException ioEx) {
							System.out.println("Error here");
						}
					}
				}
			};
			t.start();
		} else if (event.getSource().equals(okBtn)) {
			ok = true;
		} else if (event.getSource().equals(cancelBtn)) {
			cancel = true;
		}

	}

	private String[] splitMessage(String message) {
		return message.split(";");
	}

	@Override
	public void dispose() {
		System.out.println("Bye");
		super.dispose();

	}

	private class CurrentCustomerHandler implements Runnable {
		public boolean processAccept() {
			// count until time out (15s = 15000ms)
			int counter = 1;
			while (true) {
				try {
					Thread.sleep(1); // wait and listen button click
					counter++;
					// after 15s, didn't click any button -> time out
					if (counter == 15000) {
						conversationBox.append(StringConstant.TIME_OUT + "\n");
						output.println(StringConstant.CANCEL);
						try {
							socket.close();
						} catch (IOException e) {
							e.printStackTrace();
						}
						return false;
					}
					// if you click one of them -> break to raise
					// specific event below
					if (ok || cancel)
						break;

				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			// if you click ok -> confirm server that you accept
			if (ok) {
				ok = false;

				conversationBox
						.append("Step "
								+ (stepCount++)
								+ ": You accept this customer and waiting for server signal........\n");
				okBtn.setEnabled(false);
				cancelBtn.setEnabled(false);
				output.println(StringConstant.OK);
				// waiting for confirm form server if you are first or
				// not.
				message = input.nextLine();
				// if you'r first, you have to confirm server once more
				// time
				if (message.equals(StringConstant.OK)) {
					okBtn.setEnabled(true);
					cancelBtn.setEnabled(true);
					conversationBox
							.append("Step "
									+ (stepCount++)
									+ ": Congratulation, You have 15s to confirm once more time, plz!\n");
					counter = 0; // reset counter
					while (true) {
						try {
							Thread.sleep(1);
							counter++;
							// if you'r first, you have to confirm
							// server once more time
							if (counter == 15000 || cancel) {
								if (counter == 15000)
									conversationBox
											.append(StringConstant.TIME_OUT
													+ "\n");
								else {
									conversationBox.append("Step " + (stepCount++)
											+ ": You denied");
									cancel = false;
									stepCount = 1;//reset step count
								}
								// send cancel message and after that
								// close connection
								output.println(StringConstant.CANCEL);
								try {
									socket.close();
								} catch (IOException e) {
									e.printStackTrace();
								}
								// indicate you continue listening next
								// request
								return false;
							}
							if (ok) {
								output.println(StringConstant.OK);
								ok = false;
								conversationBox
										.append("Step "
								+ (stepCount++)
								+ ": Now, you click \"Picked up\" to confirm server that you have already picked up customer"
												+ "\nor \"Not found\" if you couldn't find this customer\n");
								return true;
							}

						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}

				} else if (message.equals(StringConstant.CANCEL)) {
					
					conversationBox
							.append("Step "
								+ (stepCount++)
								+ ":You were late a little seconds, good luck !!");
					stepCount = 1;//reset count
				}
			}
			if (cancel) {
				cancel = false;
				conversationBox.append("Step "
								+ (stepCount++)
								+ ":You denied\n");
				stepCount = 1;//reset step count
				output.println(StringConstant.CANCEL);
				try {
					socket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
				return false;
			}
			return false;
		}

		public boolean processPickUp() {
			while (true) {
				try {
					busy = true;

					okBtn.setText("Piked up");
					cancelBtn.setText("Not found");

					Thread.sleep(1);
					if (ok) {
						ok = false;
						cancelBtn.setEnabled(false);
						ConfirmTaxi confirmSer = new ConfirmTaxi(imei, phone,
								StatusCode.PICKED_UP_STATUS);
						confirmSer.run();
						conversationBox
								.append("Step "
								+ (stepCount++)
								+ ": You have already picked up customer\n");
						return true;
					}
					if (cancel) {
						cancel = false;
						ConfirmTaxi confirmSer = new ConfirmTaxi(imei, phone,
								StatusCode.CAN_NOT_PICKUP_STATUS);
						confirmSer.run();
						conversationBox
								.append("Step "
								+ (stepCount++)
								+ ": You can not find this customer\n");
						try {
							socket.close();
						} catch (IOException e) {
							e.printStackTrace();
						}
						return false;
					}

				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}

		public void processFinish() {
			while (true) {
				try {
					okBtn.setText("Done");
					cancelBtn.setText("Cancel");
					Thread.sleep(1);
					if (ok) {
						
						busy = false;
						ok = false;
						ConfirmTaxi confirmSer = new ConfirmTaxi(imei, phone,
								StatusCode.FINISH_STATUS);
						confirmSer.run();
						conversationBox
								.append("Step "
								+ (stepCount++)
								+ ": Good job !! You have already finished\n");
						stepCount = 1;//reset count
						try {
							socket.close();
						} catch (IOException e) {
							e.printStackTrace();
						}
						break;
					}

					// if (cancel) {
					// cancel = false;
					// conversationBox
					// .append("You can not find this customer\n");
					// }

				} catch (InterruptedException e) {
					e.printStackTrace();
				}

			}
		}

		@Override
		public void run() {
			while (true) {

				try {// /sleep to demo time out!
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}//
				conversationBox.setText("");
				conversationBox.append("Request number : " + (++count) + "\n");
				if (!isBuffer) {

					try {
						input = new Scanner(socket.getInputStream());
						output = new PrintWriter(socket.getOutputStream(), true);
						message = input.nextLine();

						String[] info = splitMessage(message);
						phone = info[0];
						address = info[1];
						coordinate = info[2] + "," + info[3];
						conversationBox.append("Customer phone : " + phone
								+ "\n");
						conversationBox.append("Address : " + address + "\n");
						conversationBox.append("Coordinate : " + coordinate
								+ "\n");
						conversationBox
								.append("Step "
								+ (stepCount++)
								+ ": Do you want to pick up this customer ?\n");
						okBtn.setText("Accept");
						cancelBtn.setText("Deny");
						okBtn.setEnabled(true);
						cancelBtn.setEnabled(true);

						// ////accept
						if (processAccept()) {

							// ///pick up

							if (processPickUp()) {
								// ///Finish
								conversationBox
										.append("Step "
								+ (stepCount++)
								+ ": Now, you click \"Done\" to confirm server because you have already finished your mission !"
												+ "\n");
								conversationBox
										.setCaretPosition(conversationBox
												.getDocument().getLength());

								processFinish();
							}

						}
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} finally {

						okBtn.setEnabled(false);
						cancelBtn.setEnabled(false);
					}
				} else {
					isBuffer = false;
					busy = false;
					phone = bufferPhone;
					address = bufferAddress;
					coordinate = bufferCoordinate;
					conversationBox.append("Customer phone : " + phone + "\n");
					conversationBox.append("Address : " + address + "\n");
					conversationBox.append("Coordinate : " + coordinate + "\n");
					conversationBox
							.append("Now, you click \"Picked up\" to confirm server that you have already picked up customer"
									+ "\nor \"Not found\" if you couldn't find this customer\n");
					okBtn.setEnabled(true);
					cancelBtn.setEnabled(true);
					if (processPickUp()) {
						// ///Finish
						conversationBox
								.append("Now, you click \"Done\" to confirm server because you have already finished your mission !"
										+ "\n");
						conversationBox.setCaretPosition(conversationBox
								.getDocument().getLength());

						processFinish();
						okBtn.setEnabled(false);
						cancelBtn.setEnabled(false);
					}

				}
				if (!isBuffer)
					break;

			}

		}

	}

	private class BufferCustomerHandler implements Runnable {

		@Override
		public void run() {
			try {
				input = new Scanner(socket.getInputStream());
				output = new PrintWriter(socket.getOutputStream(), true);
				bufferMessage = input.nextLine();
				String[] temp = splitMessage(bufferMessage);
				bufferPhone = temp[0];
				bufferAddress = temp[1];
				bufferCoordinate = temp[2] + "," + temp[3];
				String msgString = "<html>You have new request, do you want to accept this customer?<br>Phone : "
						+ bufferPhone
						+ "<br>Address : "
						+ bufferAddress
						+ "<br>Coordinate : " + bufferCoordinate + "</html>";
				final JDialog dialog = new JDialog(thisFrame, "Request", false);
				dialog.setSize(400, 140);
				// timer
				final Timer timer = new Timer(15000, new ActionListener() {
					public void actionPerformed(ActionEvent e) {
						output.println(StringConstant.CANCEL);
						dialog.dispose();
						try {
							socket.close();
						} catch (IOException e1) {
							e1.printStackTrace();
						}
					}
				});
				timer.setRepeats(false);
				timer.start();

				// ok Button
				JButton okButton = new JButton("Accept");
				okButton.addActionListener(new ActionListener() {

					@Override
					public void actionPerformed(ActionEvent arg0) {
						output.println(StringConstant.OK);
						timer.stop();
						dialog.dispose();
						ConfirmThread conThread = new ConfirmThread();
						Thread con = new Thread(conThread);
						con.start();

					}
				});

				// cancel button
				JButton cancelButton = new JButton("Deny");
				cancelButton.addActionListener(new ActionListener() {
					@Override
					public void actionPerformed(ActionEvent arg0) {
						output.println(StringConstant.CANCEL);
						dialog.dispose();
						try {
							socket.close();
						} catch (IOException e1) {
							e1.printStackTrace();
						}
					}
				});

				JPanel acceptPnl = new JPanel();
				acceptPnl.add(okButton);
				acceptPnl.add(cancelButton);

				dialog.setLocationRelativeTo(thisFrame);
				dialog.getContentPane().add(new JLabel(msgString));
				dialog.getContentPane().add(acceptPnl, BorderLayout.SOUTH);
				dialog.setVisible(true);

			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}

	}

	public class ConfirmThread implements Runnable {

		@Override
		public void run() {
			bufferMessage = input.nextLine();
			if (bufferMessage.equals(StringConstant.OK)) {
				final JDialog confirmDialog = new JDialog(thisFrame, "Confirm",
						false);
				confirmDialog.setSize(200, 100);

				// timer for confirm dialog
				final Timer timer = new Timer(15000, new ActionListener() {
					public void actionPerformed(ActionEvent e) {
						output.println(StringConstant.CANCEL);
						confirmDialog.dispose();
						try {
							socket.close();
						} catch (IOException e1) {
							e1.printStackTrace();
						}
					}
				});
				timer.setRepeats(false);
				timer.start();

				JPanel confirmPnl = new JPanel();

				// confirm button
				JButton okConfirmBtn = new JButton("Accept");
				okConfirmBtn.addActionListener(new ActionListener() {

					@Override
					public void actionPerformed(ActionEvent arg0) {
						output.println(StringConstant.OK);

						confirmDialog.dispose();
						isBuffer = true;// exist a customer
										// waiting for you

					}
				});
				confirmPnl.add(okConfirmBtn);
				// cancel confirm button
				JButton cancelConfirmBtn = new JButton("Deny");
				cancelConfirmBtn.addActionListener(new ActionListener() {

					@Override
					public void actionPerformed(ActionEvent e) {
						confirmDialog.dispose();
						output.println(StringConstant.CANCEL);
						try {
							socket.close();
						} catch (IOException e1) {
							e1.printStackTrace();
						}
					}
				});

				confirmPnl.add(cancelConfirmBtn);
				confirmDialog.getContentPane().add(
						new JLabel("Confirm one more time"));
				confirmDialog.setLocationRelativeTo(thisFrame);
				confirmDialog.add(confirmPnl, BorderLayout.SOUTH);
				confirmDialog.setVisible(true);
			} else {

			}

		}
	}

}
